#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <glib.h>
#include <crypt.h>

#include "dict.h"
#include "date.h"
#include "encrypt.h"

#define SALT_LENGTH 2
#define ENCRYPTED_NUMBER_LENGTH 20
#define DEFAULT_DATE "20000101"


#ifndef ENCRYPTED_DATE_LENGTH
#define ENCRYPTED_DATE_LENGTH 4
#endif

#include <syslog.h>

/*
 * encrypted_number_generate_with_dict, generate encrypted string for ukey_serial
 */
char* encrypted_number_generate_with_dict(const char* hw_info, const char* serial, const char* salt, struct dict_set *dicts);

/*
 * activate_number_generate_with_dict, generate activation number with date and dicts
 */
static char* activate_number_generate_with_dict(const char* register_number, const char* date, const char* _salt, struct dict_set *dicts);

/*
 * activate_number_validate_with_dict, check if activation number if valid
 * return 1 if valid or 0 if not
 */
static int activate_number_validate_with_dict(const char* hw_info, const char* serial, const char* activate_number, struct dict_set *dicts);

/*
 * activation_expire_date_with_dict, get expire date in form of struct tm from activate_number
 */
static struct tm* activation_expire_date_with_dict(const char* hw_info, const char* serial, const char* activate_number, struct dict_set *dicts);

/*
 * _activate_number_validate_with_dict, check if activation number if valid
 * return 1 if valid or 0 if not
 */
static int _activate_number_validate_with_dict(const char* register_number, const char* activate_number, struct dict_set* dicts);

/*
 * encrypted_number_generate_register, generate encrypted register number string
 * hw_info: hardware info file path
 * serial: serial number
 * salt: salt for encryption
 */

char* encrypted_number_generate_register(const char* hw_info, const char* serial, const char* salt);

/*
 * encrypted_number_generate_kyhwid, generate encrypted hardware info string
 * hw_info: hardware info file path
 * serial: serial number
 * salt: salt for encryption
 */
char* encrypted_number_generate_kyhwid(const char* hw_info, const char* serial, const char* salt);

/*
 * _adjust_encrypted_date, adjust encrypted date
 */
static char* _adjust_encrypted_date(char* encrypted_date, const char* str, 
			const char* dict, int dict_length, 
			const char* year_dict, int year_dict_length, 
			const char* yday_dict, int yday_dict_length);


/*
 * adjust_encrypted_date, adjust encrypted date
 */
char* adjust_encrypted_date(char* encrypted_date, const char* str, struct dict_set* dicts)
{
	return _adjust_encrypted_date(encrypted_date, str,
				dicts->dict, sizeof(dicts->dict),
				dicts->year_dict, sizeof(dicts->year_dict),
				dicts->yday_dict, sizeof(dicts->yday_dict));
}

/*
 * _adjust_encrypted_date, adjust encrypted date
 */
static char* _adjust_encrypted_date(char* encrypted_date, const char* str, 
			const char* dict, int dict_length, 
			const char* year_dict, int year_dict_length, 
			const char* yday_dict, int yday_dict_length)
{
	int m0, m1, m2, m3 = 0;
//	int mod_year, mod_yday;	
	char* p;

	if(!str || strlen(str) < 4 || !encrypted_date || strlen(encrypted_date) < 4)
		return encrypted_date;

	p = encrypted_date;

//	mod_year = year_dict_length / 2 + year_dict_length % 2;
//	mod_yday = yday_dict_length / 2 + yday_dict_length % 2;

//	m0 = str[0] % mod_year;
//	m1 = str[1] % mod_year;
//	m2 = str[2] % mod_yday;
//	m3 = str[3] % mod_yday;
	m0 = str[0] % year_dict_length;
	m1 = str[1] % year_dict_length;
	m2 = str[2] % yday_dict_length;
	m3 = str[3] % yday_dict_length;


	p[0] = year_dict[(index_in_dict(year_dict, year_dict_length, p[0]) + m0) % year_dict_length];
	p[1] = year_dict[(index_in_dict(year_dict, year_dict_length, p[1]) + m1) % year_dict_length];
	p[2] = yday_dict[(index_in_dict(yday_dict, yday_dict_length, p[2]) + m2) % yday_dict_length];
	p[3] = yday_dict[(index_in_dict(yday_dict, yday_dict_length, p[3]) + m3) % yday_dict_length];


	return encrypted_date;

}



/*
 * _restore_encrypted_date, restore encrypted_date from str
 * return 0 if succeed, or -1 if not
 */
static int _restore_encrypted_date(char* encrypted_date, const char* str, 
				const char* dict, int dict_length, 
				const char* year_dict, int year_dict_length, 
				const char* yday_dict, int yday_dict_length)
{
	int m0, m1, m2, m3 = 0;
//	int mod_year, mod_yday;	
	char* p;

	if(!str || strlen(str) < 4 || !encrypted_date || strlen(encrypted_date) < 4)
		return -1;

	p = encrypted_date;

//	mod_year = year_dict_length / 2 + year_dict_length % 2;
//	mod_yday = yday_dict_length / 2 + yday_dict_length % 2;

//	m0 = str[0] % mod_year;
//	m1 = str[1] % mod_year;
//	m2 = str[2] % mod_yday;
//	m3 = str[3] % mod_yday;
	m0 = str[0] % year_dict_length;
	m1 = str[1] % year_dict_length;
	m2 = str[2] % yday_dict_length;
	m3 = str[3] % yday_dict_length;

	p[0] = year_dict[(index_in_dict(year_dict, year_dict_length, p[0]) + year_dict_length - m0) % year_dict_length];
	p[1] = year_dict[(index_in_dict(year_dict, year_dict_length, p[1]) + year_dict_length - m1) % year_dict_length];
	p[2] = yday_dict[(index_in_dict(yday_dict, yday_dict_length, p[2]) + yday_dict_length - m2) % yday_dict_length];
	p[3] = yday_dict[(index_in_dict(yday_dict, yday_dict_length, p[3]) + yday_dict_length - m3) % yday_dict_length];

	return 0;

}

/*
 * restore_encrypted_date, restore encrypted_date from str
 * return 0 if succeed, or -1 if not
 */
int restore_encrypted_date(char* encrypted_date, const char* str, struct dict_set* dicts)
{
	return _restore_encrypted_date(encrypted_date, str,
				dicts->dict, sizeof(dicts->dict),
				dicts->year_dict, sizeof(dicts->year_dict),
				dicts->yday_dict, sizeof(dicts->yday_dict));
}

/*
 * adjust_string, adjust string, convert a-z to A-Z, convert '.' and '/' to character in dict
 */
static void adjust_string(char* str, const char* dict, int dict_length)
{
	int len;
	int i = 0;

	if(!str)
		return;

	len = strlen(str);

	for(i = 0; i < len; i++)
	{
		if(!char_in_dict(dict, dict_length, str[i]))
		{
			if(str[i] == '.' || str[i] == '/')
			{
				str[i] = dict[((int)str[i] + i) % dict_length];
			} else if((str[i] >= 'a' && str[i] <= 'h') || 
				(str[i] >= 'j' && str[i] <= 'n') || 
				(str[i] >= 'p' && str[i] <= 'z')) {
				str[i] -= 32;
			} else {
				str[i] = dict[str[i] % dict_length];
			}
		}
	}
	
}

/*
 * generate_rand_string, generate a rand string with length of count
 */
static char* generate_rand_string(const char* dict, int dict_length, int count)
{
	char* result;
	int i;

	result = (char*)malloc(count + 1);
	result[count] = '\0';

	srand(time(0));

	for(i = 0; i < count; i++)
	{
		int r = rand();
		result[i] = dict[r % dict_length];
	}

	return result;
}

/*
 * _encrypted_number_generate_with_dict, generate encrypted string
 */
static char* _encrypted_number_generate_with_dict(const char* key_str, const char* salt, struct dict_set* dicts)
{
	char* whole_salt = NULL;
	char* result; 
	char** raw_strings;

	if(NORMAL_DICT == dicts->dict_type)
		whole_salt = g_strconcat("$", "1", "$", salt, NULL);
	else if(UKEY_DICT == dicts->dict_type)
		whole_salt = g_strconcat("$", "5", "$", salt, NULL);

	raw_strings = g_strsplit(crypt(key_str, whole_salt), "$", -1);

	result = g_strdup(raw_strings[3]);

	g_free(whole_salt);
	g_strfreev(raw_strings);

	return result;
}

/*char* encrypted_number_generate(const char* hw_info, const char* serial, const char* salt)
{
	return encrypted_number_generate_with_dict(hw_info, serial, salt, &normal);
}*/

/*
 * encrypted_number_generate_register, generate encrypted register number string
 * hw_info: hardware info file path
 * serial: serial number
 * salt: salt for encryption
 */

char* encrypted_number_generate_register(const char* hw_info, const char* serial, const char* salt)
{
	return encrypted_number_generate_with_dict(hw_info, serial, salt, &normal);
}

/*
 * encrypted_number_generate_kyhwid, generate encrypted hardware info string
 * hw_info: hardware info file path
 * serial: serial number
 * salt: salt for encryption
 */
char* encrypted_number_generate_kyhwid(const char* hw_info, const char* serial, const char* salt)
{
	return encrypted_number_generate_with_dict(hw_info, serial, salt, &normal);
}

/*
 * encrypted_number_generate_with_dict, generate encrypted string with dictionary
 */
char* encrypted_number_generate_with_dict(const char* hw_info, const char* serial, const char* salt, struct dict_set *dicts)
{
	char* key_str;
	char* encrypted;
	char* result;
	int count, len;

	if(!serial || strlen(serial) == 0)
		return NULL;

	if(!hw_info || strlen(hw_info) == 0) {
		key_str = (char*)serial;
		encrypted = _encrypted_number_generate_with_dict(key_str, salt, dicts);
	} else {
		key_str = g_strconcat(hw_info, serial, NULL);
		encrypted = _encrypted_number_generate_with_dict(key_str, salt, dicts);
		if (key_str)
			g_free(key_str);	
	}

	if(!encrypted)
		return NULL;
	
	if(strlen(encrypted) == 0) {
		free(encrypted);
		return NULL;
	}

	adjust_string(encrypted, dicts->dict, sizeof(dicts->dict));

	count = string_count_char_in_dict(encrypted, dicts->dict, sizeof(dicts->dict));
	len = strlen(salt);

	if(count + len < 20) {
		g_free(encrypted);
		return NULL;
	}


	result = (char*)malloc(ENCRYPTED_NUMBER_LENGTH + 1);
	if(!result)
		return NULL;

	result[ENCRYPTED_NUMBER_LENGTH] = '\0';

	memcpy(result, encrypted, ENCRYPTED_NUMBER_LENGTH - len);
	memcpy(&result[ENCRYPTED_NUMBER_LENGTH - len], salt, len);

	if(encrypted) {
		g_free(encrypted);
		encrypted = NULL;
	}

	return result;
}


/*
 * activation_code_save, save activation code to file activation_file
 */
int activation_code_save(const char* file, const char* code)
{
	GError* local_error = NULL;
	
	if(!g_file_set_contents (file, code, strlen(code), &local_error))
	{
		return 0;
	}

	return 1;
}

/*
 * activation_code_load, load activation code from file activation_file
 */
char* activation_code_load(const char* file)
{
	GError* local_error = NULL;
	// char code[ENCRYPTED_NUMBER_LENGTH + 1] = {0};
	char *code = NULL;

	// code = (char*)malloc(ENCRYPTED_NUMBER_LENGTH + 1);
	// if(!code)
	// 	goto err_out;

	// code[ENCRYPTED_NUMBER_LENGTH] = '\0';

	if(!g_file_get_contents (file, &code, NULL, &local_error))
		goto err_out;

	return code;

err_out:
	if(local_error) {
		g_error_free(local_error);
		local_error = NULL;
	}

	return NULL;
}

/*static char* activate_number_generate(const char* register_number, const char* date, const char* _salt)
{
	return activate_number_generate_with_dict(register_number, date, _salt, &normal);
}*/

/*
 * _code_is_valid, check code string is valid with dictionary
 * 
 */
static int _code_is_valid(const char* code, struct dict_set* dicts)
{
	int i;

	if(!code || strlen(code) != ENCRYPTED_NUMBER_LENGTH)
		return 0;

	for(i = 0; i < strlen(code); ++i)
	{
		if(!char_in_dict(dicts->dict, sizeof(dicts->dict), code[i]))
		{
			return 0;
		}
	}

	return 1;
}

/*
 * activate_code_is_valid, check activation code string is valid with dictionary
 * 
 */
int activate_code_is_valid(const char* activate_code, struct dict_set* dicts)
{
	return _code_is_valid(activate_code, dicts);
}

int register_code_is_valid(const char* register_number)
{
	return _code_is_valid(register_number, &normal);
}

/*
 * activate_number_generate_with_dict, generate activation number with date and dicts
 */

static char* activate_number_generate_with_dict(const char* register_number, const char* date, const char* _salt, struct dict_set *dicts)
{
	char* activation;
	char* encrypted_date;
	char* salt;
	// length for salt and encrypted_date;
	int l_s, l_ed;
	int i;	


	if(!register_number || strlen(register_number) != ENCRYPTED_NUMBER_LENGTH)
	{
		return NULL;
	}

	for(i = 0; i < strlen(register_number); ++i)
	{
		if(!char_in_dict(dicts->dict, sizeof(dicts->dict), register_number[i]))
		{
			//printf("Invalid register code.\nIt contains invalid character.\n");
			//printf("Invalid register code.\n");
			return NULL;
		}
	}

	if(!_salt || strlen(_salt) == 0)
		salt = generate_rand_string(dicts->dict, sizeof(dicts->dict), SALT_LENGTH);
	else
		salt = g_strdup(_salt);

	if(!salt)
		return NULL;
	
	activation = encrypted_number_generate_with_dict(NULL, register_number, salt, dicts);
	if(!activation) {
		g_free(salt);
		return NULL;
	}
	encrypted_date = date_encrypt_with_dict(date, dicts);
	if(!encrypted_date)
	{
		g_free(salt);
		free(activation);
		return NULL;
	}

	l_s = strlen(salt);
	l_ed = strlen(encrypted_date);

	encrypted_date = adjust_encrypted_date(encrypted_date, activation, dicts);

	memcpy(&activation[ENCRYPTED_NUMBER_LENGTH - l_ed - l_s], encrypted_date, l_ed);	
	
	g_free(encrypted_date);
	g_free(salt);
	return activation;
}

/*
 * activate_number_compare, compare default_date with real_date to see if real_date is valid 
 * return 1 if valid, or 0 if not
 */
int activate_number_compare(const char* default_date, const char* real_date)
{
	int iRet = ( (strncmp(default_date, real_date, ENCRYPTED_NUMBER_LENGTH - ENCRYPTED_DATE_LENGTH - SALT_LENGTH) == 0) && 
		(strncmp(&default_date[ENCRYPTED_NUMBER_LENGTH - SALT_LENGTH], 
			&real_date[ENCRYPTED_NUMBER_LENGTH - SALT_LENGTH], SALT_LENGTH) == 0));
	if(default_date) {
		free(default_date);
		default_date = NULL;
	}
	return iRet;
}

/*
 * _activate_number_validate_with_dict, check if activation number if valid
 * return 1 if valid or 0 if not
 */
static int _activate_number_validate_with_dict(const char* register_number, const char* activate_number, struct dict_set* dicts)
{
	char* activate_str;

	if(!register_number || !activate_number)
		return 0;

	activate_str = activate_number_generate_with_dict(register_number, DEFAULT_DATE, &activate_number[ENCRYPTED_NUMBER_LENGTH - SALT_LENGTH], dicts);

	if(!activate_str)
		return 0;

	return activate_number_compare(activate_str, activate_number);
}

/*
 * activate_number_validate_normal, check if activation number is valid
 * hw_info: hardware info file path
 * serial: serial number
 * activate_number: activation number
 * return 1 if valid, or 0 if not
 */
int activate_number_validate_normal(const char* hw_info, const char* serial, const char* activate_number)
{
	return activate_number_validate_with_dict(hw_info, serial, activate_number, &normal);
}

/*
 * activate_number_validate_with_dict, check if activation number if valid
 * return 1 if valid or 0 if not
 */
static int activate_number_validate_with_dict(const char* hw_info, const char* serial, const char* activate_number, struct dict_set *dicts)
{
	char* register_number;
	int i, iRet = 0;

	if(!activate_number || strlen(activate_number) != 20)
	{
		//printf("激活码无效\n");
		return 0;
	}

	for(i = 0; i < strlen(activate_number); ++i)
	{
		if(!char_in_dict(dicts->dict, sizeof(dicts->dict), activate_number[i]))
		{
			//printf("Invalid activation code.\nIt contains invalid character.\n");
			//printf("Invalid activation code.\n");
			if(i==18 || i==19)//检测kms激活码后两位
			{
				if('1'==activate_number[i] || '0'==activate_number[i] || 'I'==activate_number[i] || 'O'==activate_number[i])
				{
					continue;
				}
			}
			return 0;

		}
	}
	
	register_number = encrypted_number_generate_register(hw_info, serial, "");

	iRet = _activate_number_validate_with_dict(register_number, activate_number, dicts);
	if (register_number) {
		free(register_number);
		register_number = NULL;
	}
	return iRet;
}

/*
 * activation_expire_date_normal, get date in form of struct tm from activation number
 * hw_info: hardware info file path
 * serial: serial number
 * activate_number: activation number
 * return date in form of struct tm 
 */
struct tm* activation_expire_date_normal(const char* hw_info, const char* serial, const char* activate_number)
{
	return activation_expire_date_with_dict(hw_info, serial, activate_number, &normal);
}

/*
 * activation_expire_date_with_dict, get expire date in form of struct tm from activate_number
 */
static struct tm* activation_expire_date_with_dict(const char* hw_info, const char* serial, const char* activate_number, struct dict_set *dicts)
{
	struct tm* expire_date_tm = NULL;

	if(!activate_number_validate_with_dict(hw_info, serial, activate_number, dicts))
		return NULL;

	char encrypted_date[ENCRYPTED_DATE_LENGTH + 1];

	encrypted_date[ENCRYPTED_DATE_LENGTH] = '\0';

	memcpy(encrypted_date, &activate_number[ENCRYPTED_NUMBER_LENGTH - ENCRYPTED_DATE_LENGTH - SALT_LENGTH], ENCRYPTED_DATE_LENGTH);

	if(-1 == restore_encrypted_date(encrypted_date, activate_number, dicts))
		return NULL;

	expire_date_tm = date_decrypt_with_dict(encrypted_date, dicts);

	return expire_date_tm;
}


/*
 * code_add_hyphen, add hyphen to code
 */
char* code_add_hyphen(const char* code)
{
	size_t len;

	if(!code || (len = strlen(code)) == 0)
		return NULL;
	char* str;
	size_t _len = len + len / 4;
	int i,j = 0;


	str = (char*)malloc(_len);
	memset(str, 0, _len);

	j = 0;
	for(i = 0; i < len; i++)
	{
		if(j != 0 && ((j + 1) % 5 == 0))
		{
			str[j] = '-';
			j++;
		}
		str[j] = code[i];
		j++;
	}

	return str;
}

/*
 * code_remove_hyphen, remove hyphen in code
 */
char* code_remove_hyphen(const char* code)
{
	size_t len;
	char* str;
	int i,j = 0;

	if(!code || (len = strlen(code)) == 0)
		return NULL;

	if(!strstr(code, "-"))
		return strdup(code);

	str = (char*)malloc(len + 1);

	memset(str, 0, len + 1);

	for(i = 0; i < len; i++)
	{
		if(code[i] == '-')
			++i;

		str[j] = code[i];
		j++;
	}

	return str;
}

#if 0

int main(int argc, char** argv)
{
	char* register_number;
	char* activation;
	int ret;
	struct tm* terminal;

	register_number = encrypted_number_generate(NULL, "43028", "");

	if(!register_number)
		return 1;

	activation = activate_number_generate(register_number, argv[1], NULL);
	if(!activation)
		return 1;



	ret = activate_number_validate(NULL, "43028", activation);

	terminal = activation_expire_date(NULL, "43028", activation);
	if(!terminal)
		return 1;

        printf("%-4d year\n", terminal->tm_year + 1900);
        printf("%-4d mon\n", terminal->tm_mon + 1);
        printf("%-4d mday\n", terminal->tm_mday);
        printf("%-4d yday\n", terminal->tm_yday);
        printf("%-4d hour\n", terminal->tm_hour);
        printf("%-4d min\n", terminal->tm_min);
	printf("%-4d sec\n", terminal->tm_sec);
        printf("%-4d wday\n", terminal->tm_wday);
        printf("%-4d isdst\n", terminal->tm_isdst);

	return 0;
}

#endif
